package com.eci.context.context.filter;

import com.eci.common.util.StringUtils;
import org.apache.poi.util.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * Description: http请求对象封装</br>
 * Date: 2019-04-17 00:30:26</br>
 *
 * @param
 * @author 墨鱼
 * @return
 * @since JDK 1.8
 */
public class RequestWrapper extends HttpServletRequestWrapper {

    public static String GBK = "GBK";
    public static String GB2312 = "GB2312";
    public static String UTF8 = "UTF-8";

    //保存流中的数据
    private byte[] data;

    private String bodyString = "";

    private Map<String, String[]> parameterMap;

    public String getParaString() {
        return paraString;
    }

    private String paraString = "";

    public String getBodyString() {
        return bodyString;
    }

    public RequestWrapper(HttpServletRequest request) {
        super(request);

        //从流中获取数据
        try {
            data = IOUtils.toByteArray(request.getInputStream());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        //判断编码格式
        if (isGBK(request)) {
            bodyString = new String(data, Charset.forName(GBK));

            initParaMap(GBK);
        } else {
            bodyString = new String(data, Charset.forName(UTF8));

            initParaMap(UTF8);
        }

        paraString = formatMap();
    }

    /**
     * 从请求头中获取字符编码集(如果有设置)
     *
     * @param request
     * @return
     */
    private boolean isGBK(HttpServletRequest request) {
        String contentType = request.getHeader("Content-Type");
        if (contentType == null) {
            return false;
        }

        if (contentType.toUpperCase().contains(GBK) ||
                contentType.toUpperCase().contains(GB2312)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Description: 序列化为key value</br>
     * Date: 2019-04-17 01:03:16</br>
     *
     * @param
     * @return
     * @author 墨鱼
     * @since JDK 1.8
     */
    private String formatMap() {

        StringBuilder finalStr = new StringBuilder();
        parameterMap.forEach((k, v) -> {
            finalStr.append(k).append("=").append(v[0]).append("&");
        });

        if (parameterMap.size() != 0) {
            finalStr.deleteCharAt(finalStr.length() - 1);
            return finalStr.toString();
        } else {
            return "";
        }

    }

    private void initParaMap(String charset) {
        if (parameterMap == null) {
            parameterMap = new HashMap<>();

            //get参数
            if (getQueryString() != null) {
                parseMap(getQueryString(), charset);
            }

            //post参数
            if (this.getHeader("Content-Type") != null && this.getHeader("Content-Type").contains("x-www-form-urlencoded")) {
                parseMap(bodyString, charset);
                bodyString = "";
            }

        } else {
            //nothing
        }
    }

    /**
     * Description: 解析请求参数</br>
     * Date: 2019-05-10 15:26:01</br>
     *
     * @param
     * @return
     * @author 墨鱼
     * @since JDK 1.8
     */
    private void parseMap(String splitStr, String charset) {
        String[] arr = splitStr.split("&");
        for (String str : arr) {
            String[] aa = str.split("=");
            if (aa.length < 2) {
                //nothing
            } else {
                String v = StringUtils.decode(aa[1], charset);
                parameterMap.put(aa[0], new String[]{v});
            }
        }
    }

    @Override
    public String[] getParameterValues(String name) {

        return parameterMap.get(name);
    }

    @Override
    public Map<String, String[]> getParameterMap() {

        return parameterMap;
    }

    @Override
    public String getParameter(String name) {

        return parameterMap.get(name) == null ? null : parameterMap.get(name)[0];


    }

    @Override
    public Enumeration<String> getParameterNames() {
        return Collections.enumeration(parameterMap.keySet());
    }


    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }


    @Override
    public ServletInputStream getInputStream() {
        //在调用getInputStream函数时，创建新的流，包含原先数据流中的信息，然后返回
        return new MyServletInputStream(new ByteArrayInputStream(data));
    }

    class MyServletInputStream extends ServletInputStream {
        private InputStream inputStream;

        public MyServletInputStream(InputStream inputStream) {
            this.inputStream = inputStream;
        }

        @Override
        public int read() throws IOException {
            return inputStream.read();
        }

        /**
         * Has the end of this InputStream been reached?
         *
         * @return <code>true</code> if all the data has been read from the stream,
         * else <code>false</code>
         * @since Servlet 3.1
         */
        @Override
        public boolean isFinished() {
            return true;
        }

        /**
         * Can data be read from this InputStream without blocking?
         * Returns  If this method is called and returns false, the container will
         * invoke {@link ReadListener#onDataAvailable()} when data is available.
         *
         * @return <code>true</code> if data can be read without blocking, else
         * <code>false</code>
         * @since Servlet 3.1
         */
        @Override
        public boolean isReady() {
            return true;
        }

        /**
         * Sets the {@link ReadListener} for this {@link ServletInputStream} and
         * thereby switches to non-blocking IO. It is only valid to switch to
         * non-blocking IO within async processing or HTTP upgrade processing.
         *
         * @param listener The non-blocking IO read listener
         * @throws IllegalStateException If this method is called if neither
         *                               async nor HTTP upgrade is in progress or
         *                               if the {@link ReadListener} has already
         *                               been set
         * @throws NullPointerException  If listener is null
         * @since Servlet 3.1
         */
        @Override
        public void setReadListener(ReadListener listener) {

        }
    }

}